////////////////////////////////////////////////////////////////////
// File:	AnimationManager.cpp
// Purpose: Encapsulates all animations used in the game by either
//			reusing or loading new animations based on a file.
//			Stores data related to an animation.
// Author:	Jay Baird
////////////////////////////////////////////////////////////////////

#include "AnimationManager.h"
#include "MeshManager.h"
#include <fstream>
#include <io.h>

// Static Variables:
AnimationManager AnimationManager::m_instance;

typedef std::vector<AnimationManager::animInfo>::iterator AnimIter;
typedef std::vector<AnimationManager::animInfo>::const_iterator AnimIterConst;

#ifndef PAA_HEADER
#define PAA_HEADER "PALPABLE_AFTERTASTE_ANIMATION_FMT"
#endif

#ifndef PAA_HEADER_VER
#define PAA_HEADER_VER 2.0f
#endif

using namespace AnimationComponent;

AnimationManager::AnimationManager()
{

}

AnimationManager::~AnimationManager()
{

}

int AnimationManager::Load(const char* fileName, int meshID)
{
	if(!fileName) return -1;

	// Check to see if the mesh is even available yet...
	if(!MeshManager::GetInstance()->IsAvailable(meshID)) return -1;
	
	// Check if the file is already loaded into memory...
	AnimIterConst iter = m_animations.begin();

	// Check if the animation exists already...
	int currID = FindAnimation(fileName);
	if(currID > -1) return currID;

	if(_access(fileName, 0) == -1) return -1;

	animInfo& newAnim = animInfo();

	// Find a un-used animation first
	unsigned animationSize = (unsigned)m_animations.size();
	for (unsigned i = 0; i < animationSize; i++)
	{
		if(!m_animations[i].used)
		{
			newAnim = m_animations[i];
			newAnim.used = true;
			break;
		}
	}

	strcpy_s(newAnim.fileName, strlen(fileName) + 1, fileName);
	Animation& anim = newAnim.anim;
//	anim.SetMeshID(meshID);

	// Load the animation
	std::ifstream fin(fileName, std::ios_base::in | std::ios_base::binary);

	char buffer[64];
	float version = 0.0f;

	// Read header
	fin.read(buffer, sizeof(PAA_HEADER) + 1);
	if(strcmp(buffer, PAA_HEADER) != 0) return -1;
	fin.read((char*)&version, sizeof(version));
	if(version != PAA_HEADER_VER) return -1;

	// Read in duration
	fin.read((char*)&anim.m_duration, sizeof(anim.m_duration));

	// Read in KeyFrames
	unsigned keyFrameCount = 0;
	unsigned numBones = MeshManager::GetInstance()->GetBoneCount(meshID);

	fin.read((char*)&keyFrameCount, sizeof(keyFrameCount));
	anim.m_keyFrames.resize(keyFrameCount);

	// Read in transforms per keyFrame (not per bone)
	// ex: KeyFrame
	//	   - keyTime
	//		 - transforms for the keyFrame (every single bone)

	for (unsigned i = 0; i < keyFrameCount; i++)
	{
		anim.m_keyFrames[i].transforms.resize(numBones);
		fin.read((char*)&anim.m_keyFrames[i].keyTime, sizeof(anim.m_keyFrames[i].keyTime));
	}

	for (unsigned currBone = 0; currBone < numBones; currBone++)
		for (unsigned currFrame = 0; currFrame < keyFrameCount; currFrame++)
			fin.read((char*)anim.m_keyFrames[currFrame].transforms[currBone].local.M(), sizeof(float) * 16);

	fin.close();

	// Traverse the Key Frames... (To set up correct transformations between parent's and children)
	const MeshComponent::Bone* pMeshBones = MeshManager::GetInstance()->GetBones(meshID);

	for (unsigned i = 0; i < keyFrameCount; i++)
		TraverseKeyFrame(0, pMeshBones, anim.m_keyFrames[i]);

	// Push back the new animation
	m_animations.push_back(newAnim);

	return (int)(m_animations.size() - 1);
}

bool AnimationManager::Split(int id, std::deque<int>& indices, Range* keyFrameRanges, unsigned keyFrameRangeCount)
{
	if(!IsAvailable(id) || !keyFrameRanges || !keyFrameRangeCount) return false;

	indices.resize(keyFrameRangeCount);

	// Check if any of the going-to-be-split animations already exist in memory
	// if they do then just reuse
	std::string* splitFileNames = new std::string[keyFrameRangeCount];
	
	char buffer[32];
	for (unsigned i = 0; i < keyFrameRangeCount; i++)
	{
		// Copy over the original filename from the animation then tack on begin/end frames
		splitFileNames[i] = m_animations[id].fileName;

		// This creates a new filename with "originalFilename.paa_begin-end"
		sprintf_s(buffer, _countof(buffer), "_%d-%d", keyFrameRanges[i].begin, keyFrameRanges[i].end);
		splitFileNames[i] += buffer;
		
		indices[i] = FindAnimation(splitFileNames[i].c_str());
	}

	animInfo splitAnim;

	for (unsigned i = 0; i < keyFrameRangeCount; i++)
	{
		if(indices[i] != -1) continue;

		// Start splitting the animation in sections...
		Animation* pA = &m_animations[id].anim;

		unsigned begin = keyFrameRanges[i].begin;
		const unsigned& end = keyFrameRanges[i].end;
		
		if(begin > end) continue;

		unsigned keyFrameSize = (unsigned)pA->m_keyFrames.size();

		if(begin >= keyFrameSize || end >= keyFrameSize) return false;

		Animation& split = splitAnim.anim;
		strcpy_s(splitAnim.fileName, splitFileNames[i].length() + 1, splitFileNames[i].c_str());
//		split.m_meshID = pA->m_meshID;
		split.m_duration = pA->m_keyFrames[end].keyTime;

		split.m_keyFrames.resize((end - begin) + 1);

		unsigned splitFrameSize = (unsigned)split.m_keyFrames.size();

		// Copy over keyFrames
		float beginKeyTime = pA->m_keyFrames[begin].keyTime;
		for (unsigned j = 0; j < splitFrameSize; j++, begin++)
		{
			split.m_keyFrames[j] = pA->m_keyFrames[begin];
			split.m_keyFrames[j].keyTime -= beginKeyTime;
		}

		// Set duration to last keyFrame
		split.m_duration = split.m_keyFrames.back().keyTime;

		m_animations.push_back(splitAnim);

		indices[i] = (int)(m_animations.size() - 1);
	}

	// Clean up memory with allocated fileNames
	if(keyFrameRangeCount > 0)
		delete[] splitFileNames;
	else delete splitFileNames;

	return true;
}

int AnimationManager::FindAnimation(const char* fileName) const
{
	// Check if the file is already loaded into memory...
	AnimIterConst iter = m_animations.begin();

	int currID = 0;
	while(iter != m_animations.end())
	{
		if((*iter).fileName && strcmp(fileName, (*iter).fileName) == 0)
			return currID;

		currID++;
		iter++;
	}

	return -1;
}

bool AnimationManager::Remove(int id)
{
	if(IsAvailable(id))
	{
		m_animations[id].used = false;
		m_animations[id].fileName[0] = '\0';

		return true;
	}
	
	return false;
}

void AnimationManager::RemoveAll()
{
	m_animations.clear();
}

void TraverseKeyFrame(unsigned index, const MeshComponent::Bone* pBones, KeyFrame& frame)
{
	matrix4& world = frame.transforms[index].world;
	matrix4& local = frame.transforms[index].local;

	// No Parent (Root)
	if(pBones[index].parent == -1)
		world = local;
	else // Have a parent so transform by the parent's world
	{
		matrix4& parentWorld = frame.transforms[pBones[index].parent].world;
		world = local * parentWorld;
	}

	// Traverse the children's key frames
	for (unsigned i = 0; i < pBones[index].childCount; i++)
		TraverseKeyFrame(pBones[index].children[i], pBones, frame);
}